﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using KBEngine;
using UnityEngine;
using ILRuntime.CLR.TypeSystem;
using ILRuntime.CLR.Method;
using Pumpkin.FrameBase;

public class UIBehaviour : MonoBehaviour
{
    [System.Serializable]
    public enum UIActionType
    {
        SUBMIT = 0,
        CLICK = 1,
        DOUBLE_CLICK = 2,
        HOVER = 3,
        PRESS = 4,
        SELECT = 5,
        SCROLL = 6,
        DRAG_START = 7,
        DRAG = 8,
        DRAG_OVER = 9,
        DRAG_OUT = 10,
        DRAG_END = 11,
        DROP = 12,
        KEY = 13,
        TOOLTIP = 14,
    }

    [System.Serializable]
    public class BindFunc
    {
        public UIActionType ActionType;
        public string Function;
    }
    protected static CrossDomainInfo crossUIObject;
    protected static string NamespaceInHotFix = "KBE_HotFix.UI.";
    protected UIEventListener listener_;
    public bool AutoBind = true;
    public bool Intercept = true;
    public string HandleClsName;

    public bool HasHotFixObject = false;
    public string HotFixObjectTag = "";

    [SerializeField] public List<BindFunc> EventHandlers = new List<BindFunc>();

    protected CrossDomainInstance crossHandler;
    private List<IMethod> submitHandlers;
    private List<IMethod> clickHandlers;
    private List<IMethod> doubleClickHandlers;
    private List<IMethod> hoverHandlers;
    private List<IMethod> pressHandlers;
    private List<IMethod> selectHandlers;
    private List<IMethod> scrollHandlers;
    private List<IMethod> dragStartHandlers;
    private List<IMethod> dragHandlers;
    private List<IMethod> dragOverHandlers;
    private List<IMethod> dragOutHandlers;
    private List<IMethod> dragEndHandlers;
    private List<IMethod> dropHandlers;
    private List<IMethod> keyHandlers;
    private List<IMethod> tooltipHandlers;

    protected static object[] param1 = new object[1];
    protected static object[] param2 = new object[2];

    protected void Awake()
    {
        listener_ = UIEventListener.Get(this.gameObject);
        if (AutoBind)
        {
            var parent = transform.parent;
            while (parent != null)
            {
                var behaviour = parent.GetComponent<UIBehaviour>();
                if (behaviour != null && !behaviour.AutoBind && behaviour.Intercept)
                {
                    HandleClsName = behaviour.HandleClsName;
                    break;
                }
                parent = parent.transform.parent;
            }

            if (parent == null)
            {
                HandleClsName = BaseCrossType.HotFixUIHandler;
            }
        }
        Debug.Assert(HandleClsName != null && HandleClsName != "");

        BindHotFixMethods();

        if(crossUIObject == null)
        {
            crossUIObject = CrossDomainInfoFactory.Instance.Create(BaseCrossType.HotFixUIObject);
        }
    }

    void BindHotFixMethods()
    {
        //经测试ILRuntime热更内部无法使用Delegate.CreateDelegate()方法，
        //而且每次直接在listener上绑定热更里面委托，需额外创建配备器
        //这里将采取的方法是直接把listener中的委托在unity主程内赋值，并在热更内部维护调用列表。
        //foreach (var handler in EventHandlers)
        //{
        //	UIUtils.UIUtils.GetInstance().BindEventHandlers(HotfixCls, handler.Function, handler.ActionType, listener_);
        //}
        BindClass();
        foreach (var handler in EventHandlers)
        {
            SetListener(handler.ActionType, handler.Function);
        }
    }

    void BindClass()
    {
        string className = NamespaceInHotFix + HandleClsName;
        //这里比较特殊，因为跳过utils的调用函数，将函数整理至这个类内，所以无需注册
        //if(CrossType.IsClassRegisted(className))
        //{
        //    foreach(var func in EventHandlers)
        //    {
        //        CrossType.RegistMethod(className, func.Function, GetHandleParamCount(func.ActionType));
        //    }
        //}
        crossHandler = CrossInstanceFactory.Instance.Create(className);
        var ins = UIUtils.UIHandler.GetInstance().GetHotFixCls(HandleClsName);
        crossHandler.SetInstance(ins);
    }

    void AddHandlers(List<IMethod> handlers, string handler, int paramCount = 1)
    {
        var method = crossHandler.CrossInfo.ClassType.GetMethod(handler, paramCount);
        if (method == null)
        {
            string error = string.Format("cant find method{0} in class {1} in dll", handler, HandleClsName);
            throw new InvalidOperationException(error);
        }

        handlers.Add(method);
    }

    int GetHandleParamCount(UIActionType type)
    {
        switch (type)
        {
            case UIActionType.SUBMIT:
            case UIActionType.CLICK:
            case UIActionType.DOUBLE_CLICK:
            case UIActionType.DRAG_START:
            case UIActionType.DRAG_OVER:
            case UIActionType.DRAG_OUT:
            case UIActionType.DRAG_END:
                return 1;
            case UIActionType.HOVER:
            case UIActionType.PRESS:
            case UIActionType.SELECT:
            case UIActionType.SCROLL:
            case UIActionType.DRAG:
            case UIActionType.DROP:
            case UIActionType.KEY:
            case UIActionType.TOOLTIP:
                return 2;
        }
        return 1;
    }

    void SetListener(UIActionType type, string handler)
    {
        List<IMethod> handlers = null;
        switch (type)
        {
            case UIActionType.SUBMIT:
                if (listener_.onSubmit == null)
                    listener_.onSubmit = go => onUIBehaviourEventHandlers(submitHandlers, go);
                if (submitHandlers == null)
                    submitHandlers = new List<IMethod>();
                handlers = submitHandlers;
                break;
            case UIActionType.CLICK:
                if (listener_.onClick == null)
                    listener_.onClick = go => onUIBehaviourEventHandlers(clickHandlers, go);
                if (clickHandlers == null)
                    clickHandlers = new List<IMethod>();
                handlers = clickHandlers;
                break;
            case UIActionType.DOUBLE_CLICK:
                if (listener_.onDoubleClick == null)
                    listener_.onDoubleClick = go => onUIBehaviourEventHandlers(doubleClickHandlers, go);
                if (doubleClickHandlers == null)
                    doubleClickHandlers = new List<IMethod>();
                handlers = doubleClickHandlers;
                break;
            case UIActionType.HOVER:
                if (listener_.onHover == null)
                    listener_.onHover = (go, p1) => onUIBehaviourEventHandlers(hoverHandlers, go, p1);
                if (hoverHandlers == null)
                    hoverHandlers = new List<IMethod>();
                handlers = hoverHandlers;
                break;
            case UIActionType.PRESS:
                if (listener_.onPress == null)
                    listener_.onPress = (go, p1) => onUIBehaviourEventHandlers(pressHandlers, go, p1);
                if (pressHandlers == null)
                    pressHandlers = new List<IMethod>();
                handlers = pressHandlers;
                break;
            case UIActionType.SELECT:
                if (listener_.onSelect == null)
                    listener_.onSelect = (go, p1) => onUIBehaviourEventHandlers(selectHandlers, go, p1);
                if (selectHandlers == null)
                    selectHandlers = new List<IMethod>();
                handlers = selectHandlers;
                break;
            case UIActionType.SCROLL:
                if (listener_.onScroll == null)
                    listener_.onScroll = (go, p1) => onUIBehaviourEventHandlers(scrollHandlers, go, p1);
                if (scrollHandlers == null)
                    scrollHandlers = new List<IMethod>();
                handlers = scrollHandlers;
                break;
            case UIActionType.DRAG_START:
                if (listener_.onDragStart == null)
                    listener_.onDragStart = go => onUIBehaviourEventHandlers(dragStartHandlers, go);
                if (dragStartHandlers == null)
                    dragStartHandlers = new List<IMethod>();
                handlers = dragStartHandlers;
                break;
            case UIActionType.DRAG:
                if (listener_.onDrag == null)
                    listener_.onDrag = (go, p1) => onUIBehaviourEventHandlers(dragHandlers, go, p1);
                if (dragHandlers == null)
                    dragHandlers = new List<IMethod>();
                handlers = dragHandlers;
                break;
            case UIActionType.DRAG_OVER:
                if (listener_.onDragOver == null)
                    listener_.onDragOver = go => onUIBehaviourEventHandlers(dragOverHandlers, go);
                if (dragOverHandlers == null)
                    dragOverHandlers = new List<IMethod>();
                handlers = dragOverHandlers;
                break;
            case UIActionType.DRAG_OUT:
                if (listener_.onDragOut == null)
                    listener_.onDragOut = go => onUIBehaviourEventHandlers(dragOutHandlers, go);
                if (dragOutHandlers == null)
                    dragOutHandlers = new List<IMethod>();
                handlers = dragOutHandlers;
                break;
            case UIActionType.DRAG_END:
                if (listener_.onDragEnd == null)
                    listener_.onDragEnd = go => onUIBehaviourEventHandlers(dragEndHandlers, go);
                if (dragEndHandlers == null)
                    dragEndHandlers = new List<IMethod>();
                handlers = dragEndHandlers;
                break;
            case UIActionType.DROP:
                if (listener_.onDrop == null)
                    listener_.onDrop = (go, p1) => onUIBehaviourEventHandlers(dropHandlers, go, p1);
                if (dropHandlers == null)
                    dropHandlers = new List<IMethod>();
                handlers = dropHandlers;
                break;
            case UIActionType.KEY:
                if (listener_.onKey == null)
                    listener_.onKey = (go, p1) => onUIBehaviourEventHandlers(keyHandlers, go, p1);
                if (keyHandlers == null)
                    keyHandlers = new List<IMethod>();
                handlers = keyHandlers;
                break;
            case UIActionType.TOOLTIP:
                if (listener_.onTooltip == null)
                    listener_.onTooltip = (go, p1) => onUIBehaviourEventHandlers(tooltipHandlers, go, p1);
                if (tooltipHandlers == null)
                    tooltipHandlers = new List<IMethod>();
                handlers = tooltipHandlers;
                break;
        }
        if(handler != null)
        {
            AddHandlers(handlers, handler, GetHandleParamCount(type));
        }
    }

    // void onUIBehaviourEventHandlers(List<IMethod> handlers, params object[] args)
    // {
    //     foreach (var method in handlers)
    //     {
    //         ILRTMain.GetDomain().Invoke(method, handleObject, args);
    //     }
    // }

    void onUIBehaviourEventHandlers(List<IMethod> handlers, GameObject go)
    {
        param1[0] = go;
        foreach (var method in handlers)
        {
            ILRTUtils.GetDomain().Invoke(method, crossHandler.Instance, param1);
        }
    }

    void onUIBehaviourEventHandlers(List<IMethod> handlers, GameObject go, object param)
    {
        param2[0] = go;
        param2[1] = param;
        foreach (var method in handlers)
        {
            ILRTUtils.GetDomain().Invoke(method, crossHandler.Instance, param2);
        }
    }

    // Use this for initialization
    protected void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {

    }
}